package com.example.day2.hiorder;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.*;

/**
 * 手动实现Stream的filter map forEach 方法
 *
 * @param <T>
 */
public class C04SimpleStream<T> {
    public static void main(String[] args) {
        List<Integer> list = List.of(1, 2, 3, 4, 5, 1, 2, 3);
//        C04SimpleStream.of(list)
//                .filter(x -> (x & 1) == 1)
//                .map(x -> x * x)
//                .forEach(System.out::println);

//        System.out.println(C04SimpleStream.of(list).reduce(0, Integer::sum));
//        System.out.println(C04SimpleStream.of(list).reduce(Integer.MAX_VALUE, Integer::min)); // 最小值
//        System.out.println(C04SimpleStream.of(list).reduce(Integer.MIN_VALUE, Integer::max)); // 最大值

        // 测试收集到set中
        HashSet<Integer> collect1 = C04SimpleStream.of(list).collect(HashSet::new, HashSet::add); // HashSet::add (set,t) -> set.add(t)
        System.out.println(collect1);

        // 测试收集到StringBuilder中
        System.out.println(C04SimpleStream.of(list).collect(StringBuilder::new, StringBuilder::append).toString());


        // 这里不能直接使用方法引用,因为StringJoiner没有无参构造和supplier对不上
        // 同理 StringJoiner的add方法接收的是要一个CharSequence这个类型的参数,所以也不能使用方法引用
        // (StringJoiner,Integer) -> void 这是我们提供的
        // (StringJoiner,CharSequence) -> void 这个需要的
        // 因此参数对应不上无法使用方法引用
        StringJoiner stringJoiner1 = C04SimpleStream.of(list).collect(() -> new StringJoiner("-"), (joiner, t) -> joiner.add(String.valueOf(t)));
        System.out.println(stringJoiner1);
        // 如果要使用方法引用可以在收集之前对集合进行转换
        StringJoiner stringJoiner2 = C04SimpleStream.of(list).map(String::valueOf).collect(() -> new StringJoiner("-"), StringJoiner::add);
        System.out.println(stringJoiner2);


        // 收集到Map中 key是值,value是出现的次数
        C04SimpleStream.of(list).collect(HashMap::new, (map, t) -> {
            if (map.containsKey(t)) {
                // 说明已经存在了
                map.put(t, (Integer) map.get(t) + 1);
            } else {
                map.put(t, 1);
            }
        }).forEach((k, v) -> System.out.println(k + ":" + v));

        System.out.println("-----------------------------");
        /*
            computeIfAbsent
            如果 key 在 map 中不存在，将 key 连同新生成的 value 值存入 map, 并返回 value
            如果 key 在 map 中存在，会返回此 key 上次的 value 值

            1, 2, 3, 4, 5, 1, 2, 3
            key     value
            1       AtomicInteger(2)
            2       AtomicInteger(2)
            3       AtomicInteger(2)
            4       AtomicInteger(1)
            5       AtomicInteger(1)
         */
        // 简化写法
        HashMap<Integer, AtomicInteger> collect = C04SimpleStream.of(list)
                .collect(HashMap::new, (map, t) -> map.computeIfAbsent(t, k -> new AtomicInteger()).getAndIncrement());
        System.out.println(collect);
    }

    // C代表容器的类型 supplier用来创建容器
    public <C> C collect(Supplier<C> supplier, BiConsumer<C, T> consumer) {
        C c = supplier.get(); // 创建了容器
        for (T t : collection) {
            consumer.accept(c, t); // 向容器中添加元素
        }
        return c;
    }

    // o表示p的初始值
    public T reduce(T o, BinaryOperator<T> operator) {
        T p = o; // 上次的合并结果
        for (T t : collection) { // t 是本次遍历的元素
            p = operator.apply(p, t);
        }
        return p;
    }

    public C04SimpleStream<T> filter(Predicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T t : collection) {
            if (predicate.test(t)) {
                result.add(t);
            }
        }
        return new C04SimpleStream<>(result);
    }

    // 注意这里返回后的流的泛型应该是转换后的u而不是 T
    public <U> C04SimpleStream<U> map(Function<T, U> function) {
        List<U> result = new ArrayList<>();
        for (T t : collection) {
            result.add(function.apply(t));
        }
        return new C04SimpleStream<>(result);
    }

    public void forEach(Consumer<T> consumer) {
        for (T t : collection) {
            consumer.accept(t);
        }
    }

    public static <T> C04SimpleStream<T> of(Collection<T> collection) {
        return new C04SimpleStream<>(collection);
    }

    private Collection<T> collection;

    private C04SimpleStream(Collection<T> collection) {
        this.collection = collection;
    }
}

